/* $Id: VBoxMPVidPn.h $ */
/** @file
 * VBox WDDM Miniport driver
 */

/*
 * Copyright (C) 2011-2020 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

#ifndef _WDDM_INCLUDE_VBOXMPVIDPN_H_
#define _WDDM_INCLUDE_VBOXMPVIDPN_H_

#include "wddm/VBoxMPTypes.h"
#include "common/VBoxMPDevExt.h"
#include "VBoxDisplay.h"

typedef struct VBOXVIDPN_PATH_ITER
{
    D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology;
    const DXGK_VIDPNTOPOLOGY_INTERFACE *pVidPnTopologyInterface;
    const D3DKMDT_VIDPN_PRESENT_PATH *pCurVidPnPathInfo;
    NTSTATUS Status;
} VBOXVIDPN_PATH_ITER;

DECLINLINE(void) VBoxVidPnPathIterInit(VBOXVIDPN_PATH_ITER *pIter,
                D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology,
                const DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface)
{
    pIter->hVidPnTopology = hVidPnTopology;
    pIter->pVidPnTopologyInterface = pVidPnTopologyInterface;
    pIter->pCurVidPnPathInfo = NULL;
    pIter->Status = STATUS_SUCCESS;
}

DECLINLINE(void) VBoxVidPnPathIterTerm(VBOXVIDPN_PATH_ITER *pIter)
{
    if (pIter->pCurVidPnPathInfo) {
        pIter->pVidPnTopologyInterface->pfnReleasePathInfo(
                pIter->hVidPnTopology, pIter->pCurVidPnPathInfo);
        pIter->pCurVidPnPathInfo = NULL;
    }
}

DECLINLINE(const D3DKMDT_VIDPN_PRESENT_PATH *) VBoxVidPnPathIterNext(
                                        VBOXVIDPN_PATH_ITER *pIter)
{
    NTSTATUS Status;
    const D3DKMDT_VIDPN_PRESENT_PATH *pCurVidPnPathInfo;

    if (!pIter->pCurVidPnPathInfo) {
        Status = pIter->pVidPnTopologyInterface->pfnAcquireFirstPathInfo(
                                pIter->hVidPnTopology, &pCurVidPnPathInfo);
    } else {
        Status = pIter->pVidPnTopologyInterface->pfnAcquireNextPathInfo(
            pIter->hVidPnTopology, pIter->pCurVidPnPathInfo, &pCurVidPnPathInfo);
    }

    if (Status == STATUS_SUCCESS) {
        if (pIter->pCurVidPnPathInfo) {
            pIter->pVidPnTopologyInterface->pfnReleasePathInfo(
                pIter->hVidPnTopology, pIter->pCurVidPnPathInfo);
        }
        pIter->pCurVidPnPathInfo = pCurVidPnPathInfo;
        return pCurVidPnPathInfo;
    }

    if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET ||
        Status == STATUS_GRAPHICS_DATASET_IS_EMPTY) {
        return NULL;
    }

    pIter->Status = Status;
    return NULL;
}

DECLINLINE(NTSTATUS)  VBoxVidPnPathIterStatus(VBOXVIDPN_PATH_ITER *pIter)
{
    return pIter->Status;
}

typedef struct VBOXVIDPN_MONITORMODE_ITER
{
    D3DKMDT_HMONITORSOURCEMODESET hVidPnModeSet;
    const DXGK_MONITORSOURCEMODESET_INTERFACE *pVidPnModeSetInterface;
    const D3DKMDT_MONITOR_SOURCE_MODE *pCurVidPnModeInfo;
    NTSTATUS Status;
} VBOXVIDPN_MONITORMODE_ITER;

DECLINLINE(void) VBoxVidPnMonitorModeIterInit(VBOXVIDPN_MONITORMODE_ITER *pIter,
              D3DKMDT_HMONITORSOURCEMODESET hVidPnModeSet,
              const DXGK_MONITORSOURCEMODESET_INTERFACE *pVidPnModeSetInterface)
{
    pIter->hVidPnModeSet = hVidPnModeSet;
    pIter->pVidPnModeSetInterface = pVidPnModeSetInterface;
    pIter->pCurVidPnModeInfo = NULL;
    pIter->Status = STATUS_SUCCESS;
}

DECLINLINE(void) VBoxVidPnMonitorModeIterTerm(VBOXVIDPN_MONITORMODE_ITER *pIter)
{
    if (pIter->pCurVidPnModeInfo) {
        pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo);
        pIter->pCurVidPnModeInfo = NULL;
    }
}

DECLINLINE(const D3DKMDT_MONITOR_SOURCE_MODE *) VBoxVidPnMonitorModeIterNext(
    VBOXVIDPN_MONITORMODE_ITER *pIter)
{
    NTSTATUS Status;
    const D3DKMDT_MONITOR_SOURCE_MODE *pCurVidPnModeInfo;

    if (!pIter->pCurVidPnModeInfo) {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireFirstModeInfo(
                                pIter->hVidPnModeSet, &pCurVidPnModeInfo);
    } else {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireNextModeInfo(
            pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo, &pCurVidPnModeInfo);
    }

    if (Status == STATUS_SUCCESS) {
        if (pIter->pCurVidPnModeInfo) {
            pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(pIter->hVidPnModeSet,
                                                              pIter->pCurVidPnModeInfo);
        }

        pIter->pCurVidPnModeInfo = pCurVidPnModeInfo;
        return pCurVidPnModeInfo;
    }

    if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET
        || Status == STATUS_GRAPHICS_DATASET_IS_EMPTY) {
        return NULL;
    }

    pIter->Status = Status;
    return NULL;
}

DECLINLINE(NTSTATUS) VBoxVidPnMonitorModeIterStatus(VBOXVIDPN_MONITORMODE_ITER *pIter)
{
    return pIter->Status;
}

typedef struct VBOXVIDPN_TARGETMODE_ITER
{
    D3DKMDT_HVIDPNTARGETMODESET hVidPnModeSet;
    const DXGK_VIDPNTARGETMODESET_INTERFACE *pVidPnModeSetInterface;
    const D3DKMDT_VIDPN_TARGET_MODE *pCurVidPnModeInfo;
    NTSTATUS Status;
} VBOXVIDPN_TARGETMODE_ITER;

DECLINLINE(void) VBoxVidPnTargetModeIterInit(VBOXVIDPN_TARGETMODE_ITER *pIter,
            D3DKMDT_HVIDPNTARGETMODESET hVidPnModeSet,
            const DXGK_VIDPNTARGETMODESET_INTERFACE *pVidPnModeSetInterface)
{
    pIter->hVidPnModeSet = hVidPnModeSet;
    pIter->pVidPnModeSetInterface = pVidPnModeSetInterface;
    pIter->pCurVidPnModeInfo = NULL;
    pIter->Status = STATUS_SUCCESS;
}

DECLINLINE(void) VBoxVidPnTargetModeIterTerm(VBOXVIDPN_TARGETMODE_ITER *pIter)
{
    if (pIter->pCurVidPnModeInfo) {
        pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo);
        pIter->pCurVidPnModeInfo = NULL;
    }
}

DECLINLINE(const D3DKMDT_VIDPN_TARGET_MODE *) VBoxVidPnTargetModeIterNext(VBOXVIDPN_TARGETMODE_ITER *pIter)
{
    NTSTATUS Status;
    const D3DKMDT_VIDPN_TARGET_MODE *pCurVidPnModeInfo;

    if (!pIter->pCurVidPnModeInfo) {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireFirstModeInfo(
                                pIter->hVidPnModeSet, &pCurVidPnModeInfo);
    } else {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireNextModeInfo(
            pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo, &pCurVidPnModeInfo);
    }

    if (Status == STATUS_SUCCESS) {
        if (pIter->pCurVidPnModeInfo) {
            pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(
                pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo);
        }

        pIter->pCurVidPnModeInfo = pCurVidPnModeInfo;
        return pCurVidPnModeInfo;
    }

    if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET
            || Status == STATUS_GRAPHICS_DATASET_IS_EMPTY) {
        return NULL;
    }

    pIter->Status = Status;
    return NULL;
}

DECLINLINE(NTSTATUS) VBoxVidPnTargetModeIterStatus(VBOXVIDPN_TARGETMODE_ITER *pIter)
{
    return pIter->Status;
}

typedef struct VBOXVIDPN_SOURCEMODE_ITER
{
    D3DKMDT_HVIDPNSOURCEMODESET hVidPnModeSet;
    const DXGK_VIDPNSOURCEMODESET_INTERFACE *pVidPnModeSetInterface;
    const D3DKMDT_VIDPN_SOURCE_MODE *pCurVidPnModeInfo;
    NTSTATUS Status;
} VBOXVIDPN_SOURCEMODE_ITER;

DECLINLINE(void) VBoxVidPnSourceModeIterInit(VBOXVIDPN_SOURCEMODE_ITER *pIter,
                D3DKMDT_HVIDPNSOURCEMODESET hVidPnModeSet,
                const DXGK_VIDPNSOURCEMODESET_INTERFACE *pVidPnModeSetInterface)
{
    pIter->hVidPnModeSet = hVidPnModeSet;
    pIter->pVidPnModeSetInterface = pVidPnModeSetInterface;
    pIter->pCurVidPnModeInfo = NULL;
    pIter->Status = STATUS_SUCCESS;
}

DECLINLINE(void) VBoxVidPnSourceModeIterTerm(VBOXVIDPN_SOURCEMODE_ITER *pIter)
{
    if (pIter->pCurVidPnModeInfo) {
        pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo);
        pIter->pCurVidPnModeInfo = NULL;
    }
}

DECLINLINE(const D3DKMDT_VIDPN_SOURCE_MODE *) VBoxVidPnSourceModeIterNext(VBOXVIDPN_SOURCEMODE_ITER *pIter)
{
    NTSTATUS Status;
    const D3DKMDT_VIDPN_SOURCE_MODE *pCurVidPnModeInfo;

    if (!pIter->pCurVidPnModeInfo) {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireFirstModeInfo(
                                pIter->hVidPnModeSet, &pCurVidPnModeInfo);
    } else {
        Status = pIter->pVidPnModeSetInterface->pfnAcquireNextModeInfo(
            pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo, &pCurVidPnModeInfo);
    }

    if (Status == STATUS_SUCCESS) {
        if (pIter->pCurVidPnModeInfo) {
            pIter->pVidPnModeSetInterface->pfnReleaseModeInfo(
                pIter->hVidPnModeSet, pIter->pCurVidPnModeInfo);
        }

        pIter->pCurVidPnModeInfo = pCurVidPnModeInfo;
        return pCurVidPnModeInfo;
    }

    if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET
            || Status == STATUS_GRAPHICS_DATASET_IS_EMPTY) {
        return NULL;
    }

    pIter->Status = Status;
    return NULL;
}

DECLINLINE(NTSTATUS) VBoxVidPnSourceModeIterStatus(VBOXVIDPN_SOURCEMODE_ITER *pIter)
{
    return pIter->Status;
}

typedef struct VBOXWDDM_TARGET_ITER
{
    PVBOXWDDM_SOURCE pSource;
    PVBOXWDDM_TARGET paTargets;
    UINT32 cTargets;
    UINT32 i;
    UINT32 c;
} VBOXWDDM_TARGET_ITER;

typedef BOOLEAN FNVBOXVIDPNENUMTARGETSFORSOURCE(PVBOXMP_DEVEXT pDevExt,
        D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, const DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface,
        CONST D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId,
        SIZE_T cTgtPaths, PVOID pContext);
typedef FNVBOXVIDPNENUMTARGETSFORSOURCE *PFNVBOXVIDPNENUMTARGETSFORSOURCE;

void VBoxVidPnStTIterInit(PVBOXWDDM_SOURCE pSource, PVBOXWDDM_TARGET paTargets,
    UINT32 cTargets, VBOXWDDM_TARGET_ITER* pIter);
PVBOXWDDM_TARGET VBoxVidPnStTIterNext(VBOXWDDM_TARGET_ITER* pIter);

void VBoxVidPnSourcesInit(PVBOXWDDM_SOURCE pSources, UINT32 cScreens,
                          UINT8 u8SyncState);
void VBoxVidPnTargetsInit(PVBOXWDDM_TARGET pTargets, UINT32 cScreens,
                          UINT8 u8SyncState);

NTSTATUS VBoxVidPnIsSupported(PVBOXMP_DEVEXT pDevExt, D3DKMDT_HVIDPN hVidPn,
                              BOOLEAN *pfSupported);
NTSTATUS VBoxVidPnRecommendFunctional(PVBOXMP_DEVEXT pDevExt,
                D3DKMDT_HVIDPN hVidPn, const VBOXWDDM_RECOMMENDVIDPN *pData);
NTSTATUS VBoxVidPnCofuncModality(PVBOXMP_DEVEXT pDevExt, D3DKMDT_HVIDPN hVidPn,
                                 D3DKMDT_ENUMCOFUNCMODALITY_PIVOT_TYPE enmPivot,
                                 const DXGK_ENUM_PIVOT *pPivot);
void VBoxVidPnDumpCofuncModalityInfo(const char *pPrefix,
                                     D3DKMDT_ENUMCOFUNCMODALITY_PIVOT_TYPE enmEnumPivotType,
                                     const DXGK_ENUM_PIVOT *pPivot, const char *pSuffix);
NTSTATUS VBoxVidPnRecommendMonitorModes(PVBOXMP_DEVEXT pDevExt,
            D3DDDI_VIDEO_PRESENT_TARGET_ID VideoPresentTargetId,
            D3DKMDT_HMONITORSOURCEMODESET hVidPnModeSet,
            const DXGK_MONITORSOURCEMODESET_INTERFACE *pVidPnModeSetInterface);
void VBoxVidPnSourcesCopy(VBOXWDDM_SOURCE *pDst, const VBOXWDDM_SOURCE *pSrc, UINT32 cScreens);
void VBoxVidPnTargetsCopy(VBOXWDDM_TARGET *pDst, const VBOXWDDM_TARGET *pSrc, UINT32 cScreens);
NTSTATUS VBoxVidPnCommitSourceModeForSrcId(PVBOXMP_DEVEXT pDevExt, const D3DKMDT_HVIDPN hDesiredVidPn,
        const DXGK_VIDPN_INTERFACE* pVidPnInterface, PVBOXWDDM_ALLOCATION pAllocation,
        D3DDDI_VIDEO_PRESENT_SOURCE_ID  VidPnSourceId, VBOXWDDM_SOURCE *paSources,
        VBOXWDDM_TARGET *paTargets, BOOLEAN bPathPowerTransition);
NTSTATUS vboxVidPnEnumTargetsForSource(PVBOXMP_DEVEXT pDevExt, D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology,
        const DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface,
        CONST D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId,
        PFNVBOXVIDPNENUMTARGETSFORSOURCE pfnCallback, PVOID pContext);
NTSTATUS VBoxVidPnCommitAll(PVBOXMP_DEVEXT pDevExt, const D3DKMDT_HVIDPN hDesiredVidPn,
        const DXGK_VIDPN_INTERFACE* pVidPnInterface, PVBOXWDDM_ALLOCATION pAllocation,
        VBOXWDDM_SOURCE *paSources, VBOXWDDM_TARGET *paTargets);
void VBoxVidPnStCleanup(PVBOXWDDM_SOURCE paSources, PVBOXWDDM_TARGET paTargets, UINT32 cScreens);
void VBoxDumpSourceTargetArrays(VBOXWDDM_SOURCE *paSources, VBOXWDDM_TARGET *paTargets, UINT32 cScreens);

#endif
